#include "EPollPoller.h"
#include "Logger.h"
#include "Channel.h"

#include <errno.h>
#include <unistd.h>
#include <assert.h>
#include <strings.h>

const int kNew = -1; // Channel成员的 index 初始化就是-1
// Channel 已添加到 Poller 中
const int kAdded = 1;

const int kDeleted = 2;

EPollPoller::EPollPoller(EventLoop* loop)
  : Poller(loop)
  , m_epollFd(::epoll_create1(EPOLL_CLOEXEC))
  , m_events(kInitEventListSize)
{
  if (m_epollFd < 0) {
    LOG_FATAL("epoll_create error: %d", errno);
  }
}
EPollPoller::~EPollPoller()
{
  close(m_epollFd);
}

/*
             EventLoop
    ChannelList     Poller
                    ChannelMap <fd, channel*>

*/

// Channel通过调用 eventloop 的方法将 channel 添加到 poller 中
void EPollPoller::updateChannel(Channel* channel)
{
  int index = channel->index();
  LOG_INFO("fd=%d events=%d index=%d \n", channel->fd(), channel->events(), index);
  int fd = channel->fd();
  if (index == kNew || index == kDeleted) {
    if (index == kNew) {
      m_channels[fd] = channel;
    } else {
      assert(m_channels.find(fd) != m_channels.end());
      assert(m_channels[fd] == channel);
    }
    channel->setIndex(kAdded);
    update(EPOLL_CTL_ADD, channel);
  } else {
    // 更新 channel 已注册的事件
    if (channel->isNoneEvent()) {
      // 忽略这个 channel
      update(EPOLL_CTL_DEL, channel);
      channel->setIndex(kDeleted);
    } else {
      update(EPOLL_CTL_MOD, channel);
    }
  }
}

void EPollPoller::removeChannel(Channel* channel)
{
  int fd = channel->fd();
  assert(m_channels.find(fd) != m_channels.end());
  assert(m_channels[fd] == channel);
  assert(channel->isNoneEvent());
  int index = channel->index();
  m_channels.erase(fd);
  channel->setIndex(kNew);
  if (index == kAdded) {
    update(EPOLL_CTL_DEL, channel); 
  }
}

void EPollPoller::update(int operation, Channel* channel)
{
  struct epoll_event event;
  bzero(&event, sizeof(event));
  event.events = channel->events();
  event.data.ptr = channel;
  int fd = channel->fd();
  int ret = ::epoll_ctl(m_epollFd, operation, fd, &event);
  if (ret < 0) {
    LOG_FATAL("epoll_ctl failed: %d", ret);
  }
}

Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels)
{
  LOG_INFO("fd total count: %lu", activeChannels->size());
  int numEvents = ::epoll_wait(m_epollFd, &*m_events.begin(), m_events.size(), timeoutMs);
  int saveErrno = errno;
  Timestamp now(Timestamp::now());
  if (numEvents > 0) {
    LOG_INFO("%d events happened \n", numEvents);
    fillActiveChannels(numEvents, activeChannels);
    if (numEvents == m_events.size()) {
      m_events.resize(m_events.size() * 2);
    } 
  } else if (numEvents == 0) {
    LOG_INFO("epoll wait timeout \n");
  } else {
    // 外部的中断
    if (saveErrno != EINTR) {
      errno = saveErrno;
      LOG_ERROR("EPollPoller::poll() errno: %d", errno);
    }
  }
  return now;
}

void EPollPoller::fillActiveChannels(int numEvents, ChannelList* activeChannels)
{
  for (int i = 0; i < numEvents; i++) {
    Channel *channel = (Channel*)m_events[i].data.ptr;
    assert(channel != nullptr);
    assert(channel->fd() >= 0);
    activeChannels->push_back(channel);
  }
}

